Skip to content

[JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints and defaults#23694

Draft
Picazsoo wants to merge 10 commits intoOpenAPITools:masterfrom
Picazsoo:bugfix/pageable-scan-resolve-allof-constraints
Draft

[JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints and defaults#23694
Picazsoo wants to merge 10 commits intoOpenAPITools:masterfrom
Picazsoo:bugfix/pageable-scan-resolve-allof-constraints

Conversation

@Picazsoo
Copy link
Copy Markdown
Contributor

@Picazsoo Picazsoo commented May 5, 2026

Functionality implemented in #23575 is a bit incomplete (it is fully functional, but can miss some edge cases). In cases when the minimum/maximum/default values live on a schema referenced via allOf, the validations/defaults fail to generate.This PR fixes the behavior to properly discover the validation minimum/maximum constraints as well as the default.

  • for default, the inline default wins (overrides any value defined deeper in the schema)
  • for maximum, the smallest maximum wins (so if maximum in allOf referenced schema is 75 and inline maximum is 50, then the resulting maximum is 50)
  • for minimum, the biggest minimum wins (so if minimum in allOf referenced schema is 100 and inline minimum is 10, then the resulting minimum is 100)

PR checklist

  • Read the contribution guidelines.
  • Pull Request title clearly describes the work in the pull request and Pull Request description provides details about how to validate the work. Missing information here may result in delayed response from the community.
  • Run the following to build the project and update samples:
    ./mvnw clean package || exit
    ./bin/generate-samples.sh ./bin/configs/*.yaml || exit
    ./bin/utils/export_docs_generators.sh || exit
    
    (For Windows users, please run the script in WSL)
    Commit all changed files.
    This is important, as CI jobs will verify all generator outputs of your HEAD commit as it would merge with master.
    These must match the expectations made by your contribution.
    You may regenerate an individual generator by passing the relevant config(s) as an argument to the script, for example ./bin/generate-samples.sh bin/configs/java*.
    IMPORTANT: Do NOT purge/delete any folders/files (e.g. tests) when regenerating the samples as manually written tests may be removed.
  • File the PR against the correct branch: master (upcoming 7.x.0 minor release - breaking changes with fallbacks), 8.0.x (breaking changes without fallbacks)
  • If your PR solves a reported issue, reference it using GitHub's linking syntax (e.g., having "fixes #123" present in the PR description)
  • If your PR is targeting a particular programming language, @mention the technical committee members, so they are more likely to review the pull request. - tagging @karismann (2019/03) @Zomzog (2019/04) @andrewemery (2019/10) @4brunu (2019/11) @yutaka0m (2020/03) @stefankoppier (2022/06) @e5l (2024/10) @dennisameling (2026/02), @cachescrubber (2022/02) @welshm (2022/02) @MelleD (2022/02) @atextor (2022/02) @manedev79 (2022/02) @javisst (2022/02) @borsch (2022/02) @banlevente (2022/02) @Zomzog (2022/09) @martin-mfg (2023/08), @wing328

Summary by cubic

Adds minSize and minPage support to pageable validation and resolves page/size constraints and defaults through $ref and allOf. Also fixes paged-model substitution and suppression when model name transforms (suffix/prefix) are used.

  • New Features

    • @ValidPageable supports minSize and minPage in Java and Kotlin; generators emit these when present.
  • Bug Fixes

    • Pageable scanning resolves maximum, minimum, and default through $ref/allOf with intersection semantics (smallest max, largest min; inline default wins) and generates @PageableDefault/@ValidPageable from shared component schemas.
    • Avoid NPE when scanning sort enums for array schemas with no items; improved array detection in sort enum scanning.
    • Substitute PagedModel<T> and suppress paged/meta schemas correctly when modelNameSuffix/modelNamePrefix or name mappings are set (registry keyed by transformed names; removals use raw names).

Written for commit 609fc87. Summary will update on new commits.

@Picazsoo Picazsoo changed the title Enhance pageable validation by adding minSize and minPage constraints Search also allOf references for constraints and defaults [JAVA-SPRING,KOTLIN-SPRING] - Enhance pageable validation by adding minSize and minPage constraints; Search also allOf references for constraints and defaults May 6, 2026
@Picazsoo Picazsoo marked this pull request as ready for review May 6, 2026 12:14
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 17 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringPageableScanUtils.java">

<violation number="1" location="modules/openapi-generator/src/main/java/org/openapitools/codegen/languages/SpringPageableScanUtils.java:165">
P2: Array schemas without `items` can now NPE here because `ModelUtils.isArraySchema()` does not guarantee `schema.getItems()` is non-null.</violation>
</file>

Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.

@Picazsoo Picazsoo marked this pull request as draft May 6, 2026 14:43
Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 6 files (changes from recent commits).

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java">

<violation number="1" location="modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java:5961">
P2: Removing @Test orphaned this regression test, so JUnit will no longer execute it.</violation>
</file>

Tip: Review your code locally with the cubic CLI to iterate faster.

}

@Test(description = "oneOf with discriminator generates thin sealed interface with Jackson annotations")
// -------------------------------------------------------------------------
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Removing @test orphaned this regression test, so JUnit will no longer execute it.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At modules/openapi-generator/src/test/java/org/openapitools/codegen/kotlin/spring/KotlinSpringServerCodegenTest.java, line 5961:

<comment>Removing @Test orphaned this regression test, so JUnit will no longer execute it.</comment>

<file context>
@@ -5958,7 +5958,61 @@ private Map<String, Object> springCloudKotlinPagedModelProps() {
     }
 
-    @Test(description = "oneOf with discriminator generates thin sealed interface with Jackson annotations")
+    // -------------------------------------------------------------------------
+    // substituteGenericPagedModel — modelNameSuffix / modelNamePrefix
+    // -------------------------------------------------------------------------
</file context>

Tip: Review your code locally with the cubic CLI to iterate faster.

@Mattias-Sehlstedt
Copy link
Copy Markdown
Contributor

Should this also cover exclusiveMinimum and exclusiveMaximum? Also note that those fields have different representations between OAS 3.0 and OAS 3.1, where 3.0 is a boolean while 3.1. allows for a value (so for OAS 3.1 it might be that minimum is entirely omitted and that only exclusiveMinimum is specificed).

Do note that it might be that the project represent them the same internally, as they do for some OAS 3.1 concepts.

@Picazsoo
Copy link
Copy Markdown
Contributor Author

Picazsoo commented May 6, 2026

Should this also cover exclusiveMinimum and exclusiveMaximum? Also note that those fields have different representations between OAS 3.0 and OAS 3.1, where 3.0 is a boolean while 3.1. allows for a value (so for OAS 3.1 it might be that minimum is entirely omitted and that only exclusiveMinimum is specificed).

Do note that it might be that the project represent them the same internally, as they do for some OAS 3.1 concepts.

Hmm, good point. I would expect that such things should be ideally already handled by the normalizer? Or is it problematic because the values are not integers?

But thanks for raising this. I see my blind attempt to centralize the logic in the ModelUtils.resolveMaximum and ModelUtils.resolveMinimum might be a bit premature. As I was considering it mostly in the context of the page/size use. That was definitely short-sighted.

@Mattias-Sehlstedt
Copy link
Copy Markdown
Contributor

I am unable to provide comments in code, since I get an external error (so might be that you are flooded with the same comment over and over later).

What I am wondering is if schema.getAllOf() != null can be done with the utils function hasAllOf.

I would also suggest that some shared concepts be broken out into methods to improve readability and reduce duplication. For example that

if (schema == null) return null;
if (schema.get$ref() != null) {
    schema = getReferencedSchema(openAPI, schema);
    if (schema == null) return null;
}

Is broken out into a "getRefSchema()" (might be that it already exists?). In the same vein also maybe the same for the if-cases.

E.g.,
resolved != null && resolved.getMinimum() != null to hasMinimum
and
result == null || resolved.getMinimum().compareTo(result) > 0 to stricterMinimum.

This would change the code from "null, null, ..., null" to
"If there is a schema (referenced or direct), that has one or more allOfs. Then go through all allOfs and take the minimum value if there is one. Keep the most strict value".

Heavily opinionated of course and goes against how everything else is done. But personally I find most of the code to be extremely hard to read when it really should not be give the trivial things that are done.

@Mattias-Sehlstedt
Copy link
Copy Markdown
Contributor

Should this also cover exclusiveMinimum and exclusiveMaximum? Also note that those fields have different representations between OAS 3.0 and OAS 3.1, where 3.0 is a boolean while 3.1. allows for a value (so for OAS 3.1 it might be that minimum is entirely omitted and that only exclusiveMinimum is specificed).
Do note that it might be that the project represent them the same internally, as they do for some OAS 3.1 concepts.

Hmm, good point. I would expect that such things should be ideally already handled by the normalizer? Or is it problematic because the values are not integers?

But thanks for raising this. I see my blind attempt to centralize the logic in the ModelUtils.resolveMaximum and ModelUtils.resolveMinimum might be a bit premature. As I was considering it mostly in the context of the page/size use. That was definitely short-sighted.

One could also reason that it is out-of-scope and that someone that utilizes exclusiveMaximum will have to extend your initial logic to support their use-case. I believe the current structure and separation of responsibility should make it at least semi-straightforward. And as always, if one aim to support everything with the first version, then one might never get a first version. 😄

@Picazsoo
Copy link
Copy Markdown
Contributor Author

Picazsoo commented May 6, 2026

No, I think this is a really good point. I will try to fix it. This might bite someone, when they decide to rely on the method later. It is called *Utils after all (-:

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants